package com.example.license.LicenseUtils;

import com.alibaba.fastjson.JSON;
import com.example.license.entity.LicenseCheckModel;
import com.example.license.entity.LicenseConstant;
import com.example.license.service.IAbstractServerInfos;
import com.example.license.service.IUserClientService;
import com.example.license.service.impl.LinuxServiceImpl;
import com.example.license.service.impl.WindowsServerImpl;
import de.schlichtherle.license.*;
import de.schlichtherle.xml.GenericCertificate;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;

/**
 * 自定义LicenseManager，用于增加额外的服务器硬件信息校验
 *
 * @author YJ
 * @date 2022/3/26
 */
@Slf4j
public class CustomLicenseManager extends LicenseManager {
	//XML编码
	private static final String XML_CHARSET = "UTF-8";
	//默认BUFSIZE
	private static final int DEFAULT_BUFSIZE = 8 * 1024;

	private IUserClientService service;

	public CustomLicenseManager() {
	}

	public CustomLicenseManager(LicenseParam param) {
		super(param);
	}

	public CustomLicenseManager(LicenseParam param, IUserClientService service) {
		super(param);
		this.service = service;
	}

	/**
	 * @return de.schlichtherle.license.LicenseContent
	 * @description: 获取当前项目证书信息
	 * @author YangJian
	 * @date 2022/4/5 14:24
	 */
	public synchronized LicenseContent getCertificateInfo() throws Exception {
		GenericCertificate certificate = getCertificate();
		final byte[] key = getLicenseKey();
		if (null == key) {
			throw new NoLicenseInstalledException(getLicenseParam().getSubject());
		}
		certificate = getPrivacyGuard().key2cert(key);
		final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
		return content;
	}

	/**
	 * @param key,notary
	 * @return de.schlichtherle.license.LicenseContent
	 * @description: 复写install安装证书方法，其中validate方法调用本类中的validate方法，校验IP地址、Mac地址等其他信息
	 * @author YangJian
	 * @date 2022/4/5 14:25
	 */
	@Override
	protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception {
		final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
		notary.verify(certificate);
		final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
		//校验IP地址、Mac地址,用户数,客户数
		this.validate(content);
		setLicenseKey(key);
		setCertificate(certificate);
		return content;
	}

	/**
	 * 复写verify方法，调用本类中的validate方法，校验IP地址、Mac地址等其他信息
	 *
	 * @param
	 * @return de.schlichtherle.license.LicenseContent
	 */
	@Override
	protected synchronized LicenseContent verify(final LicenseNotary notary)
		throws Exception {
		GenericCertificate certificate = getCertificate();
		// Load license key from preferences,
		final byte[] key = getLicenseKey();
		if (null == key) {
			throw new NoLicenseInstalledException(getLicenseParam().getSubject());
		}
		certificate = getPrivacyGuard().key2cert(key);
		notary.verify(certificate);
		final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
		this.validate(content);
		setCertificate(certificate);

		return content;
	}

	/**
	 * 复写validate方法，增加IP地址、Mac地址等其他信息校验
	 *
	 * @param content LicenseContent
	 */
	@SneakyThrows
	@Override
	protected synchronized void validate(final LicenseContent content) throws LicenseContentException {
		//1. 首先调用父类的validate方法,自动校验时间
		try {
			super.validate(content);
		} catch (Exception e) {
			DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			throw new LicenseContentException("当前证书已过期:" +
				format.format(content.getNotBefore()) + "-" +
				format.format(content.getNotAfter()));
		}
		//2. 校验自定义的License参数
		Map<String, Object> map = (Map<String, Object>) content.getExtra();
		LicenseCheckModel param = JSON.parseObject(JSON.toJSONString(map), LicenseCheckModel.class);
		if (param.getIpAddress()!=null&&param.getIpAddress().size()>0) {
			//当前服务器真实的IP信息
			List<String> ipList = getServerInfos(LicenseConstant.IP);
			log.info("当前服务器IP信息:" + ipList.toString());
			//校验IP地址
			if (!checkIpAddress(param.getIpAddress(), ipList)) {
				throw new LicenseContentException("当前服务器的IP没在授权范围内,当前服务器所有IP地址:" + ipList.toString());
			}
		}

		if (param.getMacAddress()!=null&&param.getMacAddress().size()>0) {
			//当前服务器Mac信息
			List<String> macList = getServerInfos(LicenseConstant.MAC);
			log.info("当前服务器Mac信息:" + macList.toString());
			//校验Mac地址
			if (!checkIpAddress(param.getMacAddress(), macList)) {
				throw new LicenseContentException("当前服务器的Mac地址没在授权范围内,当前服务器所有Mac地址:" + macList.toString());
			}
		}
		//自定义参数校验
		if (!StringUtils.isEmpty(param.getClientNum())) {
			//获取当前项目用户数
			boolean UserVerify = service.verifyUserNum(Integer.parseInt(param.getUserNum()));
			if (!UserVerify) {
				throw new LicenseContentException("当前项目用户数大于授权用户数量");
			}
		}
		if (!StringUtils.isEmpty(param.getClientNum())) {
			//获取当前项目客户数
			boolean clientVerify = service.verifyClientNum(Integer.parseInt(param.getClientNum()));
			if (!clientVerify) {
				throw new LicenseContentException("当前项目客户数大于授权客户数量");
			}
		}

	}

	/**
	 * 重写XMLDecoder解析XML
	 *
	 * @param encoded XML类型字符串
	 * @return java.lang.Object
	 */
	private Object load(String encoded) {
		BufferedInputStream inputStream = null;
		XMLDecoder decoder = null;
		try {
			inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));

			decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE), null, null);

			return decoder.readObject();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} finally {
			try {
				if (decoder != null) {
					decoder.close();
				}
				if (inputStream != null) {
					inputStream.close();
				}
			} catch (Exception e) {
				log.error("XMLDecoder解析XML失败", e);
			}
		}

		return null;
	}

	/**
	 * @param tag
	 * @return java.util.List<java.lang.String>
	 * @description:获取当前服务器需要额外校验的License参数
	 * @author YangJian
	 * @date 2022/4/5 15:29
	 */
	private List<String> getServerInfos(String tag) throws Exception {
		//操作系统类型
		String osName = System.getProperty(LicenseConstant.OS_NAME).toLowerCase();
		IAbstractServerInfos abstractServerInfos = null;
		//根据不同操作系统类型选择不同的数据获取方法
		if (osName.startsWith(LicenseConstant.WINDOWS)) {
			abstractServerInfos = new WindowsServerImpl();
		} else if (osName.startsWith(LicenseConstant.LINUX)) {
			abstractServerInfos = new LinuxServiceImpl();
		} else {
			//其他服务器类型
			abstractServerInfos = new LinuxServiceImpl();
		}
		if (LicenseConstant.IP.equals(tag)) {
			return abstractServerInfos.getIpAddress();
		} else if (LicenseConstant.MAC.equals(tag)) {
			return abstractServerInfos.getMacAddress();
		}
		return null;
	}

	/**
	 * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内<br/>
	 * 如果当前IP/Mac在可被允许的IP/Mac地址范围内，则返回true
	 *
	 * @return boolean
	 */
	private boolean checkIpAddress(List<String> expectedList, List<String> serverList) {
		if (serverList != null && serverList.size() > 0) {
			for (String expected : expectedList) {
				if (serverList.contains(expected.trim())) {
					return true;
				}
			}
		}
		return false;
	}
}
